# Test procedures in lib/dg.exp					-*- Tcl -*-

# Copyright (C) 2022 Free Software Foundation, Inc.
#
# This file is part of DejaGnu.
#
# DejaGnu is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DejaGnu is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with DejaGnu.  If not, see <http://www.gnu.org/licenses/>.

foreach lib { default_procs mockutil mockvfs } {
    set file $srcdir/$subdir/${lib}.tcl
    if [ file exists $file] {
	source $file
    } else {
	puts "ERROR: $file doesn't exist"
    }
}
foreach lib { utils dg } {
    set file $srcdir/../lib/${lib}.exp
    if [ file exists $file] {
	source $file
    } else {
	puts "ERROR: $file doesn't exist"
    }
}

# callbacks required by dg.exp

proc mock-dg-test { prog what flags } {
    set chan [open $prog r]
    set linum 0
    set output ""

    while { [gets $chan line] >= 0 } {
	incr linum
	if { [regexp -- {^\s*%([EWO])\s+([^\r\n{}]*)} $line -> item text] } {
	    switch -- $item {
		E { append output "$prog:$linum: error: $text\n" }
		W { append output "$prog:$linum: warning: $text\n" }
		O { append output "$text\n" }
	    }
	}
    }
    puts "<<< $prog $what $flags"
    puts -nonewline $output
    puts ">>> $prog $what $flags"
    return [list $output a.out]
}

proc mock-dg-prune { target output } {
    puts "<<< output pruning callback"
    puts "target: $target"
    puts "output:\n$output"
    puts ">>> output pruning callback"
    if { [regexp -- {--(unresolved|unsupported|untested): (.*)--} $output \
	      -> what why] } {
	return "::${what}::${why}"
    }
    return $output
}

# testing...
# each item in testlist: { name code checks... }
proc eval_tests { sicmd testlist } {
    set testlist [strip_comment_lines $testlist]
    foreach { test } $testlist {
	if { [llength $test] > 2 } {
	    eval [list test_proc_with_mocks [lindex $test 0] $sicmd \
		      [lindex $test 1]] [lrange $test 2 end]
	} else {
	    $sicmd eval [lindex $test 1]
	}
    }
}

# init call trace list
reset_mock_trace
# build test environment
create_mockvfs dg-test-vfs
create_test_interpreter dg-test-1 {
    copy_procs {
	dg-format-linenum dg-get-options dg-process-target
	dg-prms-id dg-options dg-do
	dg-error dg-warning dg-bogus dg-build
	dg-excess-errors dg-output dg-final
	dg-init dg-runtest dg-test dg-finish
	dg-trim-dirname
	grep mock-dg-test mock-dg-prune
    }
    link_procs { verbose }
    shim_procs { runtest_file_p }
    attach_vfs { dg-test-vfs }
    link_channels { stdout }
    copy_vars {
	dg-do-what-default dg-interpreter-batch-mode dg-linenum-format
	srcdir subdir
    }
    vars {
	tool mock
	target_triplet mock-target-one
	runtests { dg.test {} }
    }
    mocks {
	# minor test shims
	prune_warnings	{ text }		{ $text }
	isnative	{ }			{ 0 }
	unknown		{ args }		{ [error "unknown $args"] }
	# results collection
	pass		{ message }		{ 0 }
	fail		{ message }		{ 0 }
	xpass		{ message }		{ 0 }
	xfail		{ message }		{ 0 }
	kpass		{ bugid message }	{ 0 }
	kfail		{ bugid message }	{ 0 }
	unresolved	{ message }		{ 0 }
	unsupported	{ message }		{ 0 }
	untested	{ message }		{ 0 }
    }
}

foreach {type token line} {
    pass error		{%E foo { dg-error   "foo" "simple error" }}
    fail error		{%  foo { dg-error   "foo" "simple error" }}
    pass warning	{%W foo { dg-warning "foo" "simple warning" }}
    fail warning	{%  foo { dg-warning "foo" "simple warning" }}
    pass bogus		{%  foo { dg-bogus   "foo" "bogus message" }}
    fail bogus		{%W foo { dg-bogus   "foo" "bogus message" }}
    pass build		{%  foo { dg-build   "foo" "build failure" }}
    fail build		{%E foo { dg-build   "foo" "build failure" }}
    pass excess		{%  foo}
    fail excess		{%E extra}
} {
    create_mock_file dg-test-vfs "dg/basic-${type}-${token}" \
	"# test file for dg.exp\n$line\n"
    regsub -- {\{ dg-} $line "\n\n&" line
    regsub -- { \}\Z} $line " {target *-*-*} 2&" line
    if { $token ne "build" && $token ne "excess" } {
	create_mock_file dg-test-vfs "dg/linum-${type}-${token}" \
	    "#test file for dg.exp\n$line\n"
    }
    regsub -- {\n\n} $line "" line
    regsub -- {{target [-*]+} *[[:digit:]]*} $line \
	"{xfail mock-target-one}" line
    if { $token ne "excess" } {
	create_mock_file dg-test-vfs "dg/basic-here-x${type}-${token}" \
	    "#test file for dg.exp\n$line\n"
	regsub -- {xfail mock-target-one} $line "xfail mock-target-two" line
	create_mock_file dg-test-vfs "dg/basic-there-x${type}-${token}" \
	    "#test file for dg.exp\n$line\n"
    }
}

create_mock_file dg-test-vfs \
    dg/skip-target {
	# test file for dg.exp
	{ dg-do compile { target fake-target-null } }
	#E bogus error
    } \
    dg/skip-cross-target {
	# test file for dg.exp
	{ dg-do compile { target native } }
	#E bogus error
    }

foreach {result} { unresolved unsupported untested } {
    create_mock_file dg-test-vfs "dg/pskip-${result}" \
	"#test file for dg.exp\n%O --${result}: skip by prune--\n"
    create_mock_file dg-test-vfs "dg/pr58065/pskip-${result}" \
	"#test file\n{ dg-error foo \"never produced\" }\n\
	 %O --${result}: skip by prune--\n"
}

dg-test-1 eval {proc send_log { text } { puts $text }}

dg-test-1 eval dg-init
foreach { type token message } {
    pass error		{ test for errors, line 2	}
    fail error		{ test for errors, line 2	}
    pass warning	{ test for warnings, line 2	}
    fail warning	{ test for warnings, line 2	}
    pass bogus		{ test for bogus message	}
    fail bogus		{ test for bogus message	}
    pass build		{ test for build failure	}
    fail build		{ test for build failure	}
    pass excess		{ test for excess errors	}
    fail excess		{ test for excess errors	}
} {
    set check_calls {
	xpass ! {} xfail ! {} kpass ! {} kfail ! {}
	unresolved ! {} unsupported ! {} untested ! {}
    }
    set check_xcalls {
	pass C 1 fail ! {} kpass ! {} kfail ! {}
	unresolved ! {} unsupported ! {} untested ! {}
    }
    switch -glob -- ${type}:${token} {
	pass:excess	{ lappend check_calls fail ! {} pass C 1 }
	fail:excess	{ lappend check_calls pass ! {} fail C 1 }
	pass:*		{
	    lappend check_calls   fail ! {}  pass C 2
	    lappend check_xcalls xfail ! {} xpass C 1
	}
	fail:*		{
	    lappend check_calls   fail C 1  pass C 1
	    lappend check_xcalls xfail C 1 xpass ! {}
	}
    }
    if { $message ne "" } {
	lappend check_calls   $type 0 [list 1 ".*[string trim ${message}].*"]
	lappend check_xcalls x$type 0 [list 1 ".*[string trim ${message}].*"]
    }
    if { $token ne "excess" } {
	lappend check_calls pass
	switch -- ${type} {
	    pass { lappend check_calls 1 }
	    fail { lappend check_calls 0 }
	}
	lappend check_calls { 1 {.*test for excess errors.*} }
	lappend check_xcalls pass 0 { 1 {.*test for excess errors.*} }
    }
    test_proc_with_mocks "test with dg/basic-${type}-${token}" dg-test-1 \
	[list dg-runtest dg/basic-${type}-${token} "" ""] \
	check_calls $check_calls
    if { $token ne "excess" } {
	test_proc_with_mocks "test with dg/basic-here-x${type}-${token}" dg-test-1 \
	    [list dg-runtest dg/basic-here-x${type}-${token} "" ""] \
	    check_calls $check_xcalls
	test_proc_with_mocks "test with dg/basic-there-x${type}-${token}" dg-test-1 \
	    [list dg-runtest dg/basic-there-x${type}-${token} "" ""] \
	    check_calls $check_calls
    }
    if { $token ne "build" && $token ne "excess" } {
	test_proc_with_mocks "test with dg/linum-${type}-${token}" dg-test-1 \
	    [list dg-runtest dg/linum-${type}-${token} "" ""] \
	    check_calls $check_calls
    }
}
eval_tests dg-test-1 {
    { "test with dg/skip-target"
	{ dg-runtest dg/skip-target "" "" }
	check_calls {
	    pass ! {} fail ! {} xpass ! {} xfail ! {} kpass ! {} kfail ! {}
	    unresolved ! {} unsupported C 1 untested ! {}
	    unsupported 0 { 1 {.*dg/skip-target.*} }
	}
    }
    { "test with dg/skip-cross-target"
	{ dg-runtest dg/skip-cross-target "" "" }
	check_calls {
	    pass ! {} fail ! {} xpass ! {} xfail ! {} kpass ! {} kfail ! {}
	    unresolved ! {} unsupported C 1 untested ! {}
	    unsupported 0 { 1 {.*dg/skip-cross-target.*} }
	}
    }
}
foreach {result} { unresolved unsupported untested } {
    set check_calls {
	pass ! {} fail ! {} xpass ! {} xfail ! {} kpass ! {} kfail ! {}
    }
    switch -- $result {
	unresolved {
	    lappend check_calls unresolved C 1 unsupported ! {} untested ! {}
	}
	unsupported {
	    lappend check_calls unresolved ! {} unsupported C 1 untested ! {}
	}
	untested {
	    lappend check_calls unresolved ! {} unsupported ! {} untested C 1
	}
    }
    lappend check_calls $result 0 { 1 {.*skip by prune.*} }
    test_proc_with_mocks "test with dg/pskip-${result}" dg-test-1 \
	[list dg-runtest dg/pskip-${result} "" ""] \
	check_calls $check_calls
    test_proc_with_mocks "test with dg/pr58065/pskip-${result}" dg-test-1 \
	[list dg-runtest dg/pr58065/pskip-${result} "" ""] \
	check_calls $check_calls
}
dg-test-1 eval dg-finish


puts "END dg.test"
